#include "foobar2000.h"

void t_replaygain_config::reset()
{
    m_source_mode = source_mode_none;
    m_processing_mode = processing_mode_none;
    m_preamp_without_rg = 0;
    m_preamp_with_rg = 0;
}

audio_sample t_replaygain_config::query_scale(const file_info & p_info) const
{
    const audio_sample peak_margin = 1.0;//used to be 0.999 but it must not trigger on lossless

    audio_sample peak = peak_margin;
    audio_sample gain = 0;

    bool have_rg_gain = false, have_rg_peak = false;

    if (m_source_mode == source_mode_track || m_source_mode == source_mode_album)
    {
        replaygain_info info = p_info.get_replaygain();
        float gain_select = replaygain_info::gain_invalid, peak_select = replaygain_info::peak_invalid;
        if (m_source_mode == source_mode_track)
        {
            if (info.is_track_gain_present())
            {
                gain = info.m_track_gain;
                have_rg_gain = true;
            }
            else if (info.is_album_gain_present())
            {
                gain = info.m_album_gain;
                have_rg_gain = true;
            }
            if (info.is_track_peak_present())
            {
                peak = info.m_track_peak;
                have_rg_peak = true;
            }
            else if (info.is_album_peak_present())
            {
                peak = info.m_album_peak;
                have_rg_peak = true;
            }
        }
        else
        {
            if (info.is_album_gain_present())
            {
                gain = info.m_album_gain;
                have_rg_gain = true;
            }
            else if (info.is_track_gain_present())
            {
                gain = info.m_track_gain;
                have_rg_gain = true;
            }
            if (info.is_album_peak_present())
            {
                peak = info.m_album_peak;
                have_rg_peak = true;
            }
            else if (info.is_track_peak_present())
            {
                peak = info.m_track_peak;
                have_rg_peak = true;
            }
        }
    }

    gain += have_rg_gain ? m_preamp_with_rg : m_preamp_without_rg;

    audio_sample scale = 1.0;

    if (m_processing_mode == processing_mode_gain || m_processing_mode == processing_mode_gain_and_peak)
    {
        scale *= audio_math::gain_to_scale(gain);
    }

    if (m_processing_mode == processing_mode_peak || m_processing_mode == processing_mode_gain_and_peak)
    {
        if (scale * peak > peak_margin)
            scale = (audio_sample)(peak_margin / peak);
    }

    return scale;
}

audio_sample t_replaygain_config::query_scale(const metadb_handle_ptr & p_object) const
{
    audio_sample rv = 1.0;
    p_object->metadb_lock();
    const file_info * info;
    if (p_object->get_info_async_locked(info))
        rv = query_scale(*info);
    p_object->metadb_unlock();
    return rv;
}

audio_sample replaygain_manager::core_settings_query_scale(const file_info & p_info)
{
    t_replaygain_config temp;
    get_core_settings(temp);
    return temp.query_scale(p_info);
}

audio_sample replaygain_manager::core_settings_query_scale(const metadb_handle_ptr & info)
{
    t_replaygain_config temp;
    get_core_settings(temp);
    return temp.query_scale(info);
}

//enum t_source_mode {source_mode_none,source_mode_track,source_mode_album};
//enum t_processing_mode {processing_mode_none,processing_mode_gain,processing_mode_gain_and_peak,processing_mode_peak};
namespace
{
    class format_dbdelta
    {
    public:
        format_dbdelta(double p_val);
        operator const char*() const
        {
            return m_buffer;
        }
    private:
        pfc::string_fixed_t<128> m_buffer;
    };
    static const char * querysign(int val)
    {
        return val<0 ? "-" : val>0 ? "+" : "\xc2\xb1";
    }

    format_dbdelta::format_dbdelta(double p_val)
    {
        int val = (int)(p_val * 10);
        m_buffer << querysign(val) << (abs(val)/10) << "." << (abs(val)%10) << "dB";
    }
}
void t_replaygain_config::format_name(pfc::string_base & p_out) const
{
    switch (m_processing_mode)
    {
    case processing_mode_none:
        p_out = "None.";
        break;
    case processing_mode_gain:
        switch (m_source_mode)
        {
        case source_mode_none:
            if (m_preamp_without_rg == 0) p_out = "None.";
            else p_out = pfc::string_formatter() << "Preamp : " << format_dbdelta(m_preamp_without_rg);
            break;
        case source_mode_track:
        {
            pfc::string_formatter fmt;
            fmt << "Apply track gain";
            if (m_preamp_without_rg != 0 || m_preamp_with_rg != 0) fmt << ", with preamp";
            fmt << ".";
            p_out = fmt;
        }
        break;
        case source_mode_album:
        {
            pfc::string_formatter fmt;
            fmt << "Apply album gain";
            if (m_preamp_without_rg != 0 || m_preamp_with_rg != 0) fmt << ", with preamp";
            fmt << ".";
            p_out = fmt;
        }
        break;
        };
        break;
    case processing_mode_gain_and_peak:
        switch (m_source_mode)
        {
        case source_mode_none:
            if (m_preamp_without_rg >= 0) p_out = "None.";
            else p_out = pfc::string_formatter() << "Preamp : " << format_dbdelta(m_preamp_without_rg);
            break;
        case source_mode_track:
        {
            pfc::string_formatter fmt;
            fmt << "Apply track gain";
            if (m_preamp_without_rg != 0 || m_preamp_with_rg != 0) fmt << ", with preamp";
            fmt << ", prevent clipping according to track peak.";
            p_out = fmt;
        }
        break;
        case source_mode_album:
        {
            pfc::string_formatter fmt;
            fmt << "Apply album gain";
            if (m_preamp_without_rg != 0 || m_preamp_with_rg != 0) fmt << ", with preamp";
            fmt << ", prevent clipping according to album peak.";
            p_out = fmt;
        }
        break;
        };
        break;
    case processing_mode_peak:
        switch (m_source_mode)
        {
        case source_mode_none:
            p_out = "None.";
            break;
        case source_mode_track:
            p_out = "Prevent clipping according to track peak.";
            break;
        case source_mode_album:
            p_out = "Prevent clipping according to album peak.";
            break;
        };
        break;
    }
}

bool t_replaygain_config::is_active() const
{
    switch (m_processing_mode)
    {
    case processing_mode_none:
        return false;
    case processing_mode_gain:
        switch (m_source_mode)
        {
        case source_mode_none:
            return m_preamp_without_rg != 0;
        case source_mode_track:
            return true;
        case source_mode_album:
            return true;
        };
        return false;
    case processing_mode_gain_and_peak:
        switch (m_source_mode)
        {
        case source_mode_none:
            return m_preamp_without_rg < 0;
        case source_mode_track:
            return true;
        case source_mode_album:
            return true;
        };
        return false;
    case processing_mode_peak:
        switch (m_source_mode)
        {
        case source_mode_none:
            return false;
        case source_mode_track:
            return true;
        case source_mode_album:
            return true;
        };
        return false;
    default:
        return false;
    }
}
